import sys
from PyQt5 import QtWidgets, uic, QtGui, QtCore
from PyQt5.QtCore import QTimer, QThread
from functools import partial
import cv2 as cv
import datetime
from pathlib import Path

cameraParam = {
    "videoWidth": 1920,
    "videoHeight": 1080,
    "videoFPS": 30
}
Ui_MainWindow, QtBaseClass = uic.loadUiType('window.ui')

class FrameCaptureWorker(QtCore.QObject):
    # 定义信号，用于发送帧数据
    frame_captured = QtCore.pyqtSignal(object)  # 发送帧数据
    finished = QtCore.pyqtSignal()  # 工作完成信号

    def __init__(self):
        super().__init__()
        self.running = True
        self.capture = cv.VideoCapture(0)
        # 设置视频捕获参数
        if not self.capture.isOpened():
            raise RuntimeError("无法打开摄像头")

        self.video_width = cameraParam["videoWidth"]
        self.video_height = cameraParam["videoHeight"]
        self.video_fps = cameraParam["videoFPS"]

        self.capture.set(cv.CAP_PROP_FRAME_WIDTH, self.video_width)
        self.capture.set(cv.CAP_PROP_FRAME_HEIGHT, self.video_height)
        self.capture.set(cv.CAP_PROP_FPS, self.video_fps)
    
        self.time_interval = int(1000 / self.video_fps)  # 帧间隔时间（毫秒）
    def reload_capture(self, width, height, fps):
        if self.video_fps != fps or self.video_width != width or self.video_height != height:
            self.video_width = width
            self.video_height = height
            self.video_fps = fps

            self.capture.set(cv.CAP_PROP_FRAME_WIDTH, width)
            self.capture.set(cv.CAP_PROP_FRAME_HEIGHT, height)
            self.capture.set(cv.CAP_PROP_FPS, fps)

    def capture_frames(self):
        """捕获帧并发送信号"""
        self.timer = QTimer()
        self.timer.timeout.connect(self.capture_frames)
        self.timer.start(self.time_interval)

        def _get_frame():
            if self.running:
                start = datetime.datetime.now().timestamp()
                ret, frame = self.capture.read()
                # 控制帧率
                end = datetime.datetime.now().timestamp()
                elapsed = (end - start) * 1000  # 转换为毫秒
                delay = max(int(self.time_interval - elapsed), 1)
                if ret:
                    # 发送帧到主线程
                    self.frame_captured.emit(frame)
                self.timer.start(delay)
            else:
                self.timer.stop()
        _get_frame()
    
    def stop(self):
        """停止捕获"""
        self.running = False
        self.capture.release()
        self.finished.emit()

class VideoPlayer(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.mode = 0  # 默认模式为相机
        self.video_source = 0  # 默认摄像头索引

        self.available_modes = ["camera", "video"]  # 可用模式列表
        self.available_btn = [self.cameraBtn, self.videoBtn]  # 按钮列表
        # 绑定按钮事件
        self.lastBtn.clicked.connect(partial(self.change_mode, self.mode-1))
        self.nextBtn.clicked.connect(partial(self.change_mode, self.mode+1))
        self.cameraBtn.clicked.connect(partial(self.change_mode, 0, True))
        self.videoBtn.clicked.connect(partial(self.change_mode, 1, True))

        self.recordOnBtn.clicked.connect(partial(self.change_recording_status, "resume"))
        self.recordPauseBtn.clicked.connect(partial(self.change_recording_status, "pause"))
        self.recordStopBtn.clicked.connect(partial(self.change_recording_status, "stop"))

        # 创建场景
        self.scene = QtWidgets.QGraphicsScene(self.graphicsView)
        self.graphicsView.setScene(self.scene)
        
        # 初始化 pixmap item（用于显示图像）
        self.pixmap_item = QtWidgets.QGraphicsPixmapItem()
        self.scene.addItem(self.pixmap_item)
        
        # save
        self.save_dir = Path("./image")
        self.save_dir.mkdir(parents=True, exist_ok=True)
        self.recordStart = False

        self.recordFeatureWidget.setVisible(False)  # 隐藏录像功能相关控件
        self.frameIndex = 0
        # 设置视频捕获工作线程
        self.setup_capture_thread()
        
    def setup_capture_thread(self):
        """设置视频捕获线程"""
        # 创建线程和工作对象
        self.capture_thread = QThread()
        self.capture_worker = FrameCaptureWorker()
        
        # 将工作对象移动到线程
        self.capture_worker.moveToThread(self.capture_thread)
        
        # 连接信号和槽
        self.capture_thread.started.connect(self.capture_worker.capture_frames)
        self.capture_worker.frame_captured.connect(self.display_frame)
        self.capture_worker.finished.connect(self.capture_thread.quit)
        self.capture_worker.finished.connect(self.capture_worker.deleteLater)
        self.capture_thread.finished.connect(self.capture_thread.deleteLater)
        
        # 启动线程
        self.capture_thread.start()

    def climp(self, value, min_value, max_value):
        """限制值在指定范围内"""
        return max(min_value, min(value, max_value))

    def draw_highlight(self):
        # 更新按钮样式
        for i, btn in enumerate(self.available_btn):
            if i == self.mode:
                btn.setFlat(False)
            else:
                btn.setFlat(True)
    
    def change_mode(self, mode, force = False):
        mode_tmp = self.climp(mode, 0, len(self.available_modes)-1)
        if mode_tmp == self.mode and force:
            self.trig_frature()
            return
        self.mode = mode_tmp
        self.draw_highlight()

    def take_photo(self):
        """拍照功能"""
        # 格式化时间
        timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        cv.imwrite(str(self.save_dir.joinpath(f"{timestamp}.png")), self.frame)

    def start_record(self):
        """开始录像功能"""
        # 格式化时间
        self.recordStart = True
        self.recordPause = False
        timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        self.video_path = self.save_dir.joinpath(f"{timestamp}.mp4")
        
        # 定义视频编码器和输出文件
        fourcc = cv.VideoWriter_fourcc(*'mp4v')
        self.out = cv.VideoWriter(str(self.video_path), fourcc, cameraParam["videoFPS"], (cameraParam["videoWidth"], cameraParam["videoHeight"]))
        self.recordTimeLabel.setText("录制时间: 0.00秒")
        self.recordStartTime = self.lastRecordTime = datetime.datetime.now().timestamp()
        self.recording_time = 0.0
        # 禁用设置按键
        self.settingBtn.setEnabled(False)
        
    def stop_record(self):
        self.recordStart = False
        self.out.release()
        self.settingBtn.setEnabled(True)

    def _recording_save(self, frame):
        if not self.recordStart:
            return
        if not self.recordPause:
            temp = datetime.datetime.now().timestamp() - self.lastRecordTime
            self.recording_time += temp
            self.recordTimeLabel.setText(f"录制时间: {self.recording_time:.2f}秒")
            self.out.write(frame)
        self.lastRecordTime = datetime.datetime.now().timestamp()

    def change_recording_status(self, status):
        if status == "pause":
            """暂停录像"""
            self.recordPause = True
            self.hide_and_disable_button(self.recordPauseBtn)
            self.show_and_enable_button(self.recordOnBtn)
        elif status == "resume":
            """恢复录像"""
            self.recordPause = False
            self.show_and_enable_button(self.recordPauseBtn)
            self.hide_and_disable_button(self.recordOnBtn)
        elif status == "stop":
            """停止录像"""
            self.stop_record()
            self.recordFeatureWidget.setVisible(False)
            self.cameraFeatureWidget.setVisible(True)
            self.show_and_enable_button(self.recordOnBtn)
            self.show_and_enable_button(self.recordPauseBtn)
    # 隐藏并禁用特定按钮
    def hide_and_disable_button(self, button):
        """隐藏并禁用按钮"""
        button.setVisible(False)  # 隐藏按钮
        button.setEnabled(False)   # 禁用按钮

    # 显示并启用特定按钮
    def show_and_enable_button(self, button):
        """显示并启用按钮"""
        button.setVisible(True)   # 显示按钮
        button.setEnabled(True)   # 启用按钮

    def trig_frature(self):
        """触发当前模式的操作"""
        if self.mode == 0:
            self.take_photo()
        elif self.mode == 1:
            self.start_record()
            self.recordFeatureWidget.setVisible(True)
            self.cameraFeatureWidget.setVisible(False)

            self.recordOnBtn.setVisible(False)

    def display_frame(self, frame):
        """
        在 graphicsView 中显示图像帧
        :param frame: OpenCV 图像 (numpy array)
        """
        self.frame = frame
        # 将 OpenCV 的 BGR 图像转换为 RGB
        rgb_image = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
        
        # 转换为 QImage
        h, w, ch = rgb_image.shape
        bytes_per_line = ch * w
        qt_image = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
        
        # 转换为 QPixmap 并显示
        pixmap = QtGui.QPixmap.fromImage(qt_image)
        self.pixmap_item.setPixmap(pixmap)
        
        # 调整视图大小以适应图像
        self.graphicsView.fitInView(self.pixmap_item, QtCore.Qt.KeepAspectRatio)

        self._recording_save(frame)

        
    def closeEvent(self, event):
        """关闭窗口时停止线程"""
        if hasattr(self, 'capture_worker'):
            self.capture_worker.stop()
        if hasattr(self, 'capture_thread'):
            self.capture_thread.quit()
            self.capture_thread.wait()
        self.recordStart = False
        event.accept()

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = VideoPlayer()
    window.show()
    sys.exit(app.exec_())